package com.fary.chapter01_aqs.ext_semaphore;

import com.fary.chapter01_aqs.ext_aqs.FaryAbstractQueuedSynchronizer;

import java.io.Serializable;


/**
 *	一个计数信号量。从概念上讲，信号量维护了一个许可集。
 *	如有必要，在许可可用前会阻塞每一个 acquire()，然后再获取该许可。
 *	每个 release() 添加一个许可，从而可能释放一个正在阻塞的获取者。
 *	但是，不使用实际的许可对象，Semaphore 只对可用许可的号码进行计数，并采取相应的行动。
 *	Semaphore 通常用于限制可以访问某些资源（物理或逻辑的）的线程数目。
 *	获得一项前，每个线程必须从信号量获取许可，从而保证可以使用该项。
 *	该线程结束后，将项返回到池中并将许可返回到该信号量，从而允许其他线程获取该项。
 *
 *	注意，调用 acquire() 时无法保持同步锁，因为这会阻止将项返回到池中。
 *	信号量封装所需的同步，以限制对池的访问，这同维持该池本身一致性所需的同步是分开的。
 *	将信号量初始化为 1，使得它在使用时最多只有一个可用的许可，从而可用作一个相互排斥的锁。
 *	这通常也称为二进制信号量，因为它只能有两种状态：一个可用的许可，或零个可用的许可。
 *	按此方式使用时，二进制信号量具有某种属性（与很多 Lock 实现不同），即可以由线程释放“锁”，
 *	而不是由所有者（因为信号量没有所有权的概念）。在某些专门的上下文（如死锁恢复）中这会很有用。
 *	此类的构造方法可选地接受一个公平 参数。当设置为 false 时，此类不对线程获取许可的顺序做任何保证。
 *	特别地，闯入 是允许的，也就是说可以在已经等待的线程前为调用 acquire() 的线程分配一个许可，
 *	从逻辑上说，就是新线程将自己置于等待线程队列的头部。当公平设置为 true 时，
 *	信号量保证对于任何调用获取方法的线程而言，都按照处理它们调用这些方法的顺序（即先进先出；FIFO）
 *	来选择线程、获得许可。
 *
 *	注意，FIFO 排序必然应用到这些方法内的指定内部执行点。 所以，可能某个线程先于另一个线程调用了 acquire，
 *	但是却在该线程之后到达排序点，并且从方法返回时也类似。
 *
 *	还要注意，非同步的 tryAcquire 方法不使用公平设置，而是使用任意可用的许可。
 *	通常，应该将用于控制资源访问的信号量初始化为公平的，以确保所有线程都可访问资源。
 *	为其他的种类的同步控制使用信号量时，非公平排序的吞吐量优势通常要比公平考虑更为重要。
 *	此类还提供便捷的方法来同时 acquire 和释放多个许可。小心，在未将公平设置为 true 时使用这些方法会
 *	增加不确定延期的风险。
 *	内存一致性效果：线程中调用“释放”方法（比如 release()）之前的操作 happen-before
 *	另一线程中紧跟在成功的“获取”方法（比如 acquire()）之后的操作。
 */
public class FarySemaphore implements Serializable {

    private static final long serialVersionUID = -3222578661600680210L;
    /**
     * 所有机制都通过AbstractQueuedSynchronizer子类实现
     */
    private final FarySync sync;

    /**
     * 创建具有给定的许可数和非公平的公平设置的 Semaphore。
     * @param permits	 初始的可用许可数目。此值可能为负数，在这种情况下，必须在授予任何获取前进行释放。
     */
    public FarySemaphore(int permits) {
        sync = new FaryNonfairSync(permits);
    }
    /**
     * 创建具有给定的许可数和给定的公平设置的 Semaphore。
     * @param permits	初始的可用许可数目。此值可能为负数，在这种情况下，必须在授予任何获取前进行释放。
     * @param fair	如果此信号量保证在争用时按先进先出的顺序授予许可，则为 true；否则为 false。
     */
    public FarySemaphore(int permits, boolean fair) {
        sync = fair ? new FaryFairSync(permits) : new FaryNonfairSync(permits);
    }

    /**
     * 从此信号量获取一个许可，在提供一个许可前一直将线程阻塞，否则线程被中断。
     * 获取一个许可（如果提供了一个）并立即返回，将可用的许可数减 1。
     *
     * 如果没有可用的许可，则在发生以下两种情况之一前，禁止将当前线程用于线程安排目的并使其处于休眠状态：
     * 	某些其他线程调用此信号量的 release() 方法，并且当前线程是下一个要被分配许可的线程；
     * 	或者其他某些线程中断当前线程。
     *
     * 如果当前线程：
     * 	被此方法将其已中断状态设置为 on ；
     * 	或者 在等待许可时被中断。
     * 则抛出 InterruptedException，并且清除当前线程的已中断状态。
     * @throws InterruptedException	如果当前线程被中断
     */
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    public void release() {
        sync.releaseShared(1);
    }

    /**
     * 信号量的同步实现。使用AQS状态表示许可证。子类化为公平和非公平版本。
     */
    abstract static class FarySync extends FaryAbstractQueuedSynchronizer {

        private static final long serialVersionUID = 1192457210091910933L;

        FarySync(int permits) {
            // 设置同步状态的值
            setState(permits);
        }
        /**
         * 返回此信号量中当前可用的许可数。
         * @return	此信号量中的可用许可数
         */
        final int getPermits() {
            return getState();
        }

        /**
         * 非公平锁
         * 仅在调用时此信号量中有给定数目的许可时，才从此信号量中获取这些许可。
         */
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                // 返回同步状态的当前值。
                int available = getState();
                // 正在同步-要获取的许可数
                int remaining = available - acquires;
                // 如果前面减去的结果小于0，或者cas的方式设置state成功，则退出
                if (remaining < 0 || compareAndSetState(available, remaining))
                    return remaining;
            }
        }

        /**
         * 如果允许释放许可，则返回 true；否则返回 false
         */
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                // 返回同步状态的当前值
                int current = getState();
                // 同步数+释放数
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }
        /**
         * 根据指定的缩减量减小可用许可的数目。
         * 此方法在使用信号量来跟踪那些变为不可用资源的子类中很有用。
         */
        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();	// 返回同步状态的当前值。
                int next = current - reductions;	// 同步数-释放数
                if (next > current)
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))// 当前状态值等于期望值（CAS）
                    return;
            }
        }

        /**
         * 获取并返回立即可用的所有许可。
         * @return	获取的许可数
         */
        final int drainPermits() {
            for (;;) {
                int current = getState();	// 返回同步状态的当前值。
                // 同步数为0或者当前状态值等于期望值（CAS）
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }


    }

    /**
     * 非公平
     */
    static final class FaryNonfairSync extends FarySync {
        private static final long serialVersionUID = -2694183684443567898L;

        /**
         *	创建具有给定的许可数和非公平设置的 Semaphore。
         * @param permits	初始的可用许可数目。此值可能为负数，在这种情况下，必须在授予任何获取前进行释放。
         */
        FaryNonfairSync(int permits) {
            super(permits);
        }

        /**
         * 非公平
         * 仅在调用时此信号量中有给定数目的许可时，才从此信号量中获取这些许可。
         * @param acquires	要获取的许可数
         * @return	获取给定数目的许可（如果提供了）并立即返回
         */
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

    /**
     * 公平的版本
     */
    static final class FaryFairSync extends FarySync {
        private static final long serialVersionUID = 2014338818796000944L;

        /**
         * 建具有给定的许可数和公平设置的 Semaphore。
         * @param permits	初始的可用许可数目。此值可能为负数，在这种情况下，必须在授予任何获取前进行释放。
         */
        FaryFairSync(int permits) {
            super(permits);
        }

        /**
         * 公平
         * 仅在调用时此信号量中有给定数目的许可时，才从此信号量中获取这些许可。
         * @param acquires	要获取的许可数
         * @return	获取给定数目的许可（如果提供了）并立即返回
         */
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 || compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }
}